package br.ufpe.cin.imlm2.jbeat.soot.throwableset.propagation;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import soot.PhaseOptions;
import soot.jimple.paddle.PaddleHook;
import soot.jimple.spark.SparkTransformer;
import soot.jimple.toolkits.callgraph.CHATransformer;

import br.ufpe.cin.imlm2.jbeat.soot.common.JBeatOptions;
import br.ufpe.cin.imlm2.util.options.NoSuchOptionException;
import br.ufpe.cin.imlm2.util.options.Option;
import br.ufpe.cin.imlm2.util.options.Option.OptionBuilder;

public class PropagationOptions extends JBeatOptions {

	public static final String FILTERS = "filters";
	public static final String FILTER_GENERATED = "generated";
	public static final String FILTER_THROWN = "thrown";
	public static final String FILTER_UNCHECKED = "unchecked";
	public static final String FILTER_CHECKED = "checked";
	public static final String FILTER_APPLICATION = "application";
	public static final String FILTER_LIBRARY = "library";
	public static final String CALLGRAPH = "callGraph";
	private static String CURRENT_CG = "cha";
	public static final String STATIC_INITIALIZERS = "staticInitializers";
	public static final String SYNTHETIC_ENTITIES = "syntheticMethods";

	public PropagationOptions() {
		super();
		List<Object> allowedStrings = new ArrayList<Object>();
		allowedStrings.add("cha");
		allowedStrings.add("spark");
		allowedStrings.add("paddle");
		this.options.add(new Option.OptionBuilder("callGraph", "cha",
				String.class, null)
				.alias("cg")
				.description(
						"Callgraph type to be used. Allowed values are 'cha', 'spark' and "
								+ "'paddle'.").allowedValues(allowedStrings)
				.build());
		this.options.add(new Option(STATIC_INITIALIZERS, "clinits",
				"Defines whether or not static initializers (clinits) should be considered"
						+ " in the analysis.", Boolean.FALSE, Boolean.class,
				null));
		this.options
				.add(new Option(
						SYNTHETIC_ENTITIES,
						"synthetics",
						"Defines whether or not synthetic methods should be considered in the analysis.",
						Boolean.FALSE, Boolean.class, null));

		List<Object> allowedFilters = new ArrayList<Object>();
		allowedFilters.add(FILTER_APPLICATION);
		allowedFilters.add(FILTER_CHECKED);
		allowedFilters.add(FILTER_UNCHECKED);
		allowedFilters.add(FILTER_THROWN);
		allowedFilters.add(FILTER_GENERATED);
		allowedFilters.add(FILTER_LIBRARY);
		Option option = new OptionBuilder(FILTERS, new ArrayList<String>(),
				List.class, String.class).allowedValues(allowedFilters).build();
		String defValues = "'" + FILTER_APPLICATION + "', " + "'"
				+ FILTER_CHECKED + "', " + "'" + FILTER_GENERATED + "', " + "'"
				+ FILTER_LIBRARY + "', " + "'" + FILTER_THROWN + "' and " + "'"
				+ FILTER_UNCHECKED + "'";
		option.setDescription("A list of exception filters.Exceptions will be filtered out"
				+ " during propagation depending on the listed filters. Possible values are any "
				+ "combination of " + defValues + ".");
		this.options.add(option);

	}

	private void switchCG(String value) {
		if (value.equalsIgnoreCase(PropagationOptions.CURRENT_CG))
			return;

		if (value.equalsIgnoreCase("cha")) {
			CHATransformer.v().transform("cg.cha",
					PhaseOptions.v().getPhaseOptions("cg.cha"));
			CURRENT_CG = "cha";
		} else if (value.equalsIgnoreCase("spark")) {
			SparkTransformer.v().transform("cg.spark",
					PhaseOptions.v().getPhaseOptions("cg.spark"));
			CURRENT_CG = "spark";
		} else if (value.equalsIgnoreCase("paddle")) {
			PaddleHook.v().transform("cg.paddle",
					PhaseOptions.v().getPhaseOptions("cg.paddle"));
			CURRENT_CG = "paddle";
		}
	}

	@Override
	public void parse(Map<String, String> options) throws NoSuchOptionException {
		super.parse(options);
		if (CURRENT_CG != this.getCallgraph()) {
			this.switchCG(this.getCallgraph());
		}
	}

	public String getCallgraph() {
		return (String) this.getOption(CALLGRAPH).getValue();
	}

	public boolean isStatic_initializers() {
		return (Boolean) this.getOption(STATIC_INITIALIZERS).getValue();
	}

	public boolean isSynthetic_entities() {
		return (Boolean) this.getOption(SYNTHETIC_ENTITIES).getValue();
	}

	@SuppressWarnings(FILTER_UNCHECKED)
	public List<String> getFilters() {
		return (List<String>) this.getOption(FILTERS).getValue();
	}

}
